#ifndef _TCP_SERVICE__
#define _TCP_SERVICE__

#include <cerrno>
#include <arpa/inet.h>
#include <cstring>
#include <sys/wait.h>
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "ThreadPool/Log.hpp"
#include "ThreadPool/ThreadPool.hpp"
#include "ThreadPool/Task.hpp"
#include <pthread.h>
#include <signal.h>
#include <unistd.h>
#include <memory>

#define BACKLOG 20

typedef ThreadPool<Task_add*> threadpool;

struct PthreadData
{
    std::string _ip;
    uint16_t _port;
    int _actual_socket;
};


void server_dispose(int real_socket, std::string& goal_ip, int goal_port)
{
    char buff[1024];
    while (1)
    {
        bzero(buff, sizeof buff);
        ssize_t ret = recv(real_socket, buff, (sizeof buff) - 1, 0);
     if (ret < 0 )
     {
            //出现错误
        Logmessage(FATIL, "read falt");
        close(ret);
        break;
     }else if (ret == 0)
     {
        //管道关闭
        Logmessage(FATIL, "客户端ip[%s] 端口号[%d] 断开连接", goal_ip.c_str(), goal_port);
        close(ret);
        break;
     }else
      {
        //读取成功,数据回发
        buff[ret] = 0;
        Logmessage(NOWAIN, "ip:[%s] port:[%d]# %s", goal_ip.c_str(), goal_port, buff);
        send(real_socket, buff, ret, 0);
        // printf(" %s\n", buff);
     }
    }
}


class Tcp_service
{
public:
    Tcp_service(uint16_t port, threadpool* thr_pool, std::string addr = "")
        :port_(port), address_(addr) , _thr_pool(thr_pool)
    {}

    ~Tcp_service()
    {}

    void service_init()
    {
        //1. 创建套接字
        listen_socket_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listen_socket_ < 0)
        {
            Logmessage(FATIL, "socket fatil%d %s", errno, strerror(errno));
            exit(1);
        }
        //2. 初始化addre结构
        struct sockaddr_in goal_;
        bzero(&goal_, sizeof goal_);
        goal_.sin_family = AF_INET;
        goal_.sin_port = htons(port_);
        goal_.sin_addr.s_addr = address_.empty() ? INADDR_ANY : inet_addr(address_.c_str()); 
        //3. 建立连接通道

        if (bind(listen_socket_, (sockaddr*)&goal_, sizeof goal_) < 0)
        {
            Logmessage(FATIL, "bind fatil%d %s", errno, strerror(errno));
            exit(2);
        }

        // 等待监听中
        if ( listen(listen_socket_, BACKLOG) < 0)
        {
            Logmessage(FATIL, "listen fatil %d %s", errno, strerror(errno));
            exit(3); 
        }

        Logmessage(NOWAIN, "SERVICE SUCCESS %d %s", errno, strerror(errno));
    }

    void start()
    {
        // signal(SIGCHLD, SIG_IGN);
        //循环接受信息
        while (1)
        {
        //1. 接受请求
        struct sockaddr_in send_;  //请求方信息
        bzero(&send_, sizeof send_);
        socklen_t len = sizeof send_;
        // accept会等待请求方申请，会处于阻塞状态
        int actual_socket = accept(listen_socket_, (sockaddr*)&send_, &len);
        if ( actual_socket < 0 )
        {
            Logmessage(FATIL, "accept fail%d %s", errno, strerror(errno));
            continue;
        }
        // 连接成功
        std::string send_ip = inet_ntoa(send_.sin_addr);
        uint16_t send_port = ntohs(send_.sin_port);
        Logmessage(NOWAIN, "连接成功,客户端为 ip:%s 端口号：%d", send_ip.c_str(), send_port);

        // (1.0)服务器处理信息——单进程版本
        // //2. 分析处理数据
        // server_dispose(actual_socket, send_ip, send_port);

        // // (2.0) 优化——子进程版本
        // pid_t pd = fork();
        // if (pd == 0)
        // {
        //     //2. 分析处理数据
        //     close(listen_socket_); //子进程拷贝一份父进程的文件描述服表
        //     server_dispose(actual_socket, send_ip, send_port);
        //     exit(0);
        // }
        // close(actual_socket); // 子进程里保留了该文件描述符，父进程已经不需要了
        // // 按照曾经的理解，现在应该让父进程进行等待子进程，但多少都存在些问题。
        // // 1. waitpid阻塞式等待，不就跟单线程一样？
        // // 2. 非阻塞式等待，需要构建子进程管理结构比较麻烦，而且我们不需要关心子进程的返回情况。
        // // 因此我们可以采用信号知识，忽略子进程返回。
        // // 操作细则：在service启动时 signal(SIGCHLD, SIG_IGN);
        
        // // (2.1) ———— 子进程退出，孙子进程让1接管
        // pid_t pd = fork();
        // if (pd == 0)
        // {
        //     //2. 分析处理数据
        //     close(listen_socket_); //子进程拷贝一份父进程的文件描述服表
        //     if (fork() > 0) exit(0); // 孙子进程变成孤儿进程，让bash接管
        //     server_dispose(actual_socket, send_ip, send_port);
        //     exit(0);
        // }
        // waitpid(pd, nullptr, 0); // 子进程进入立马退出，父进程几乎不阻塞等待

        // // (3.0) ———— 多线程版本
        // pthread_t it = -1;  // 线程的标识号先默认为1，后面在设置。
        // PthreadData* data =  new PthreadData;
        // data->_ip = send_ip;
        // data->_port = send_port;
        // data->_actual_socket = actual_socket;
        // pthread_create(&it, nullptr, pth_service, (void*)data);

        // (4.0) ———— 启用线程池
        // 让线程来进行对网络端的信息进行处理
        Task_add* task = new Task_add(actual_socket, send_port, send_ip);
        _thr_pool->push(task);

        // 交换策略：服务端未被占满时，来一条就交换任务队列
        if (_thr_pool->Get_queue_task_size() == 0 
        && _thr_pool->Get_queue_task_reserver_size() != 0)
        {
        _thr_pool->swap_queue();
        }
        }
    }
private:

    static void* pth_service(void* args)
    {
        PthreadData* data = static_cast<PthreadData*>(args);
        // 进来先剥离线程，这样主线程不用等待返回
        pthread_detach(pthread_self());
        server_dispose(data->_actual_socket, data->_ip, data->_port);
        close(data->_actual_socket);
        delete data;
        return nullptr;
    }


    int listen_socket_ = -1; // 这里的套接字形容为外面接客
    uint16_t port_;
    int actual_socket = -1;
    std::string address_;

    threadpool* _thr_pool;
};


#endif







